from django.db import models
from django.db.models import Q, F
from django.shortcuts import reverse
# 第三方工具包，富文本编辑器
from ckeditor.fields import RichTextField

from users.models import User


# Create your models here.
# 请尽量慎用文章模型的修改时间字段，用不好会出现意想不到的bug
# emmm，之前的注释当我没说，现在想想看院系部门板块的功能接近饱和，继续添加重复的功能已经没有太大的意义
class Campus(models.Model):
    """
    校区模型
    本模型提供如下字段：
    校区名称
    校区简介
    校区当前状态
    校区成立时间
    """
    STATUS_NORMAL = 1
    STATUS_HIDDEN = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, "正常"),
        (STATUS_HIDDEN, "隐藏")
    )
    name = models.CharField(max_length=40, verbose_name="校区名称")
    desc = models.CharField(max_length=500, verbose_name="校区简介")
    status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS, verbose_name="状态")
    created_time = models.DateField(verbose_name="成立时间")

    # 社团或者部门不同于文章的创建时间可以被自动填充为当前时间，这就需要在设置信息的时候添加合理性验证
    # 嘻嘻，成立时间验证已经完成啦，详情请见validations.valid_created_time

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = verbose_name_plural = "校区"
        ordering = ['-created_time']

    @classmethod
    def get_all_campuses(cls):
        """获取当前所有状态正常的校区对象"""
        return cls.objects.filter(Q(status=cls.STATUS_NORMAL))

    @classmethod
    def get_campus(cls, campus_id):
        """通过查找id的方式获取指定且状态正常的校区对象"""
        try:
            # 听说model.objects.filter具有缓存功能，会将最近一次筛选结果缓存提高查找速率
            # 但鉴于当前类方法的目的是查找单个结果，所以暂且觉得用filter方法返回结果不太合适
            return cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=campus_id)
            )
        except cls.DoesNotExist:
            return None


class College(models.Model):
    """
    院系模型
    本模型提供如下字段：
    院系名称
    院系简介
    院系所属校区
    院系当前状态
    院系成立时间
    """
    STATUS_NORMAL = 1
    STATUS_HIDDEN = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, "正常"),
        (STATUS_HIDDEN, "隐藏")
    )
    name = models.CharField(max_length=40, verbose_name="院系名称")
    desc = models.CharField(max_length=500, verbose_name="院系简介")
    campus = models.ForeignKey(Campus, on_delete=models.CASCADE, verbose_name='所属校区')
    status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS, verbose_name="状态")
    created_time = models.DateField(verbose_name="成立时间")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = verbose_name_plural = "院系"
        ordering = ['-created_time']

    @classmethod
    def get_all_colleges(cls):
        return cls.objects.filter(Q(status=cls.STATUS_NORMAL))

    @classmethod
    def get_college(cls, college_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=college_id)
            )
        except cls.DoesNotExist:
            return None


class Department(models.Model):
    """
    部门模型
    本模型提供如下字段：
    部门名称
    部门简介
    部门介绍
    部门logo图标
    部门所属院系
    部门当前状态
    部门成立时间
    部门人数
    部门联系电话
    电子邮箱
    部门负责人姓名
    """
    STATUS_NORMAL = 1
    STATUS_HIDDEN = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, "公开"),
        (STATUS_HIDDEN, "隐藏")
    )
    name = models.CharField(max_length=40, verbose_name="部门名称")
    desc = models.CharField(max_length=500, verbose_name="部门简介")
    # config_name使用指定的配置样式名，默认使用default样式名
    content = RichTextField(config_name='departments_config', verbose_name="部门介绍")
    image = models.ImageField(upload_to='logo', verbose_name="部门图标")
    owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="管理员")
    college = models.ForeignKey(College, on_delete=models.CASCADE, verbose_name="所属院系")
    status = models.PositiveIntegerField(default=STATUS_HIDDEN, choices=STATUS_ITEMS, verbose_name="状态")
    created_time = models.DateField(verbose_name="成立时间")
    number = models.PositiveIntegerField(default=0, verbose_name="部门人数")
    contact = models.CharField(default="", max_length=20, blank=True, verbose_name="联系电话")
    email = models.EmailField(default="", blank=True, verbose_name="电子邮箱")
    principal = models.CharField(default="", max_length=20, blank=True, verbose_name="部门负责人")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = verbose_name_plural = "部门"
        ordering = ['-created_time']

    @classmethod
    def get_all_normal_departments(cls):
        return cls.objects.filter(Q(status=cls.STATUS_NORMAL))

    @classmethod
    def get_all_hidden_departments(cls):
        return cls.objects.filter(Q(status=cls.STATUS_HIDDEN))

    @classmethod
    def get_user_normal_departments(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_NORMAL) & Q(owner__id=user_id)
        )

    @classmethod
    def get_user_hidden_departments(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_HIDDEN) & Q(owner__id=user_id)
        )

    @classmethod
    def get_department(cls, department_id):
        try:
            return cls.objects.get(
                Q(id=department_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_normal_department(cls, department_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=department_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_hidden_department(cls, department_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_HIDDEN) & Q(id=department_id)
            )
        except cls.DoesNotExist:
            return None

    def get_absolute_url(self):
        return reverse('departments:department_info', args=[self.id, ])


class Association(models.Model):
    """
    社团模型
    本模型提供如下字段：
    社团名称
    社团简介
    社团介绍
    社团logo图标
    社团所属校区
    社团当前状态
    社团成立时间
    社团人数
    所属校区
    联系电话
    电子邮箱
    社团负责人姓名
    """
    STATUS_NORMAL = 1
    STATUS_HIDDEN = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, "公开"),
        (STATUS_HIDDEN, "隐藏")
    )
    name = models.CharField(max_length=40, verbose_name="社团名称")
    desc = models.CharField(max_length=500, verbose_name="社团简介")
    content = RichTextField(config_name='departments_config',verbose_name="社团介绍")
    image = models.ImageField(upload_to='logo', verbose_name="社团logo图标")
    owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="管理员")
    campus = models.ForeignKey(Campus, on_delete=models.CASCADE, verbose_name="所属校区")
    status = models.PositiveIntegerField(default=STATUS_HIDDEN, choices=STATUS_ITEMS, verbose_name="状态")
    created_time = models.DateField(verbose_name="成立时间")
    number = models.PositiveIntegerField(default=0, verbose_name="社团人数")
    contact = models.CharField(default="", max_length=20, blank=True, verbose_name="联系电话")
    email = models.EmailField(default="", blank=True, verbose_name="电子邮箱")
    principal = models.CharField(default="", max_length=20, blank=True, verbose_name="社团负责人")

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = verbose_name_plural = "社团"
        ordering = ['-created_time']

    @classmethod
    def get_all_normal_associations(cls):
        return cls.objects.filter(Q(status=cls.STATUS_NORMAL))

    @classmethod
    def get_all_hidden_associations(cls):
        return cls.objects.filter(Q(status=cls.STATUS_HIDDEN))

    @classmethod
    def get_user_normal_associations(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_NORMAL) & Q(owner__id=user_id)
        )

    @classmethod
    def get_user_hidden_associations(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_HIDDEN) & Q(owner__id=user_id)
        )

    @classmethod
    def get_association(cls, association_id):
        try:
            return cls.objects.get(
                Q(id=association_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_normal_association(cls, association_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=association_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_hidden_association(cls, association_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_HIDDEN) & Q(id=association_id)
            )
        except cls.DoesNotExist:
            return None

    def get_absolute_url(self):
        return reverse('departments:association_info', args=[self.id, ])


class DepartmentPost(models.Model):
    """
    部门文章模型
    本模型提供如下字段：
    所属部门
    文章作者
    文章标题
    文章摘要
    文章内容
    文章当前状态
    文章创建时间
    文章最近修改时间
    文章浏览次数
    文章点赞次数
    """
    STATUS_NORMAL = 1
    STATUS_HIDDEN = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, "发表"),
        (STATUS_HIDDEN, "草稿")
    )

    department = models.ForeignKey(Department, on_delete=models.CASCADE, verbose_name="所属部门")
    author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
    title = models.CharField(max_length=40, verbose_name="标题")
    desc = models.CharField(max_length=200, blank=True, null=True, verbose_name="文章摘要")
    content = RichTextField(config_name='departments_config',verbose_name="文章内容")
    status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS, verbose_name="状态")
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    modified_time = models.DateTimeField(auto_now=True, verbose_name="修改时间")
    nov = models.PositiveIntegerField(default=0, verbose_name="浏览次数")
    nol = models.PositiveIntegerField(default=0, verbose_name="点赞次数")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = verbose_name_plural = "部门文章"
        ordering = ['-created_time']

    @classmethod
    def get_all_normal_posts(cls):
        return cls.objects.filter(Q(status=cls.STATUS_NORMAL))

    @classmethod
    def get_all_hidden_posts(cls):
        return cls.objects.filter(Q(status=cls.STATUS_HIDDEN))

    @classmethod
    def get_user_normal_posts(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_NORMAL) & Q(author__id=user_id)
        )

    @classmethod
    def get_user_hidden_posts(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_HIDDEN) & Q(author__id=user_id)
        )

    @classmethod
    def get_post(cls, post_id):
        try:
            return cls.objects.get(
                Q(id=post_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_normal_post(cls, post_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_hidden_post(cls, post_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_HIDDEN) & Q(id=post_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def nov_increased(cls, post_id):
        """
        使用django.db.models F()让文章浏览量自增1，可以避免冲突
        从逻辑上来讲，只有对外可见的文章才能被用户浏览，所以暂时不会响应对隐藏文章的操作请求
        注意！如果需要获取最新的文章浏览量数据，执行完该操作之后，清重新从数据库加载对应信息
        如果已读取对应数据至内存后需要进行修改，建议直接使用save()提交，不使用本方法
        """
        try:
            target = cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
            target.nov = F('nov') + 1
            target.save()
            return True
        except cls.DoesNotExist:
            return False

    @classmethod
    def nol_increased(cls, post_id):
        """点赞，文章点赞次数+1"""
        try:
            target = cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
            target.nol = F('nol') + 1
            target.save()
            return True
        except cls.DoesNotExist:
            return False

    @classmethod
    def nol_decreased(cls, post_id):
        """取消点赞，文章点赞次数-1"""
        try:
            target = cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
            target.nol = F('nol') - 1
            target.save()
            return True
        except cls.DoesNotExist:
            return False

    def self_nov_cleared(self):
        """文章浏览次数清零，特别用于文章的实例对象"""
        self.nov = 0
        self.save(update_fields=['nov', ])

    def self_nol_cleared(self):
        """文章点赞次数清零，特别用于文章的实例对象"""
        self.nol = 0
        self.save(update_fields=['nol', ])

    def get_absolute_url(self):
        """使用模型对象重定向url"""
        return reverse('departments:department_post_info', args=[self.id, ])


class AssociationPost(models.Model):
    """
    社团文章模型
    本模型提供如下字段：
    所属社团
    文章作者
    文章标题
    文章摘要
    文章内容
    文章当前状态
    文章创建时间
    文章最近修改时间
    文章浏览次数
    文章点赞次数
    """
    STATUS_NORMAL = 1
    STATUS_HIDDEN = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, "发表"),
        (STATUS_HIDDEN, "草稿")
    )
    association = models.ForeignKey(Association, on_delete=models.CASCADE, verbose_name="所属社团")
    author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
    title = models.CharField(max_length=40, verbose_name="标题")
    desc = models.CharField(max_length=200, blank=True, null=True, verbose_name="文章摘要")
    content = RichTextField(config_name='departments_config',verbose_name="文章内容")
    status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS, verbose_name="状态")
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    modified_time = models.DateTimeField(auto_now=True, verbose_name="修改时间")
    nov = models.PositiveIntegerField(default=0, verbose_name="浏览次数")
    nol = models.PositiveIntegerField(default=0, verbose_name="点赞次数")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = verbose_name_plural = "社团文章"
        ordering = ['-created_time']

    @classmethod
    def get_all_normal_posts(cls):
        return cls.objects.filter(Q(status=cls.STATUS_NORMAL))

    @classmethod
    def get_all_hidden_posts(cls):
        return cls.objects.filter(Q(status=cls.STATUS_HIDDEN))

    @classmethod
    def get_user_normal_posts(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_NORMAL) & Q(author__id=user_id)
        )

    @classmethod
    def get_user_hidden_posts(cls, user_id):
        return cls.objects.filter(
            Q(status=cls.STATUS_HIDDEN) & Q(author__id=user_id)
        )

    @classmethod
    def get_post(cls, post_id):
        try:
            return cls.objects.get(
                Q(id=post_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_normal_post(cls, post_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def get_hidden_post(cls, post_id):
        try:
            return cls.objects.get(
                Q(status=cls.STATUS_HIDDEN) & Q(id=post_id)
            )
        except cls.DoesNotExist:
            return None

    @classmethod
    def nov_increased(cls, post_id):
        try:
            target = cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
            target.nov = F('nov') + 1
            target.save()
            return True
        except cls.DoesNotExist:
            return False

    @classmethod
    def nol_increased(cls, post_id):
        try:
            target = cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
            target.nol = F('nol') + 1
            target.save()
            return True
        except cls.DoesNotExist:
            return False

    @classmethod
    def nol_decreased(cls, post_id):
        try:
            target = cls.objects.get(
                Q(status=cls.STATUS_NORMAL) & Q(id=post_id)
            )
            target.nol = F('nol') - 1
            target.save()
            return True
        except cls.DoesNotExist:
            return False

    def self_nov_cleared(self):
        self.nov = 0
        self.save(update_fields=['nov', ])

    def self_nol_cleared(self):
        self.nol = 0
        self.save(update_fields=['nol', ])

    def get_absolute_url(self):
        return reverse('departments:association_post_info', args=[self.id, ])

# 多余注释都已经删掉了，只保留必要的注释，这样看上去可能会舒服些:-)
